package io.terminus.spzx.manager.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import io.terminus.spzx.common.config.exception.SpzxCustomException;
import io.terminus.spzx.consts.UserInfoConstant;
import io.terminus.spzx.manager.mapper.SysRoleUserMapper;
import io.terminus.spzx.manager.mapper.SysUserMapper;
import io.terminus.spzx.manager.service.SysUserService;
import io.terminus.spzx.model.dto.system.AssignRoleDto;
import io.terminus.spzx.model.dto.system.LoginDto;
import io.terminus.spzx.model.dto.system.SysUserDto;
import io.terminus.spzx.model.entity.system.SysUser;
import io.terminus.spzx.model.vo.common.ResultCodeEnum;
import io.terminus.spzx.model.vo.system.LoginVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @Author lichunlong lcl253137@alibaba-inc.com
 * @Desc 系统用户Service实现类
 * @Date 2024-03-12
 */
@Service
public class SysUserServiceImpl implements SysUserService {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private SysRoleUserMapper sysRoleUserMapper;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public LoginVo login(LoginDto loginDto) {
        // pre步骤: 校验图片验证码是否OK
        checkValidateCode(loginDto);

        // 1、获取提交的用户名
        String userName = loginDto.getUserName();

        // 2、根据用户名查询数据库表: sys_user
        SysUser sysUser = sysUserMapper.selectUserInfoByUserName(userName);

        // 3、如果用户不存在，则返回错误信息
        if (Objects.isNull(sysUser)) {
            throw new SpzxCustomException(ResultCodeEnum.LOGIN_ERROR);
        }

        // 4、获取输入的密码，比较输入的密码和数据库密码是否一致
        String rdsPassword = sysUser.getPassword();
        // 比较前需要对前端输入的密码做加密处理
        String inputPassword = DigestUtils.md5DigestAsHex(loginDto.getPassword().getBytes());

        if (!StringUtils.equals(rdsPassword, inputPassword)) {
            throw new SpzxCustomException(ResultCodeEnum.LOGIN_ERROR);
        }

        // 5、登录成功，生成用户唯一Token标识
        String token = UUID.randomUUID().toString().replaceAll("-", StringUtils.EMPTY);

        // 6、把登录成功用户信息放到redis里面（key: token   value: 用户信息）
        redisTemplate.opsForValue().set(UserInfoConstant.REDIS_USERINFO_TOKEN_KEY_PREFIX + token, JSON.toJSONString(sysUser), 7, TimeUnit.DAYS);

        // 7、返回loginVo对象
        LoginVo loginVo = new LoginVo();
        loginVo.setToken(token);

        return loginVo;
    }

    @Override
    public void logout(String token) {
        redisTemplate.delete(UserInfoConstant.REDIS_USERINFO_TOKEN_KEY_PREFIX + token);
    }

    @Override
    public SysUser getUserInfo(String token) {
        String userJson = redisTemplate.opsForValue().get(UserInfoConstant.REDIS_USERINFO_TOKEN_KEY_PREFIX + token);

        return JSON.parseObject(userJson, SysUser.class);
    }

    @Override
    public PageInfo<SysUser> findByPage(SysUserDto sysUserDto, Integer current, Integer limit) {
        // 设置分页参数
        PageHelper.startPage(current, limit);

        // 查询满足条件的数据
        List<SysUser> sysUserList = sysUserMapper.findByPage(sysUserDto);

        // 封装PageInfo对象
        return new PageInfo<>(sysUserList);
    }

    @Override
    public void saveSysUser(SysUser sysUser) {
        // 1、判断userName用户名不能重复
        if (Objects.nonNull(sysUserMapper.selectUserInfoByUserName(sysUser.getUserName()))) {
            throw new SpzxCustomException(ResultCodeEnum.USER_NAME_IS_EXISTS);
        }

        // 2、密码加密
        sysUser.setPassword(DigestUtils.md5DigestAsHex(sysUser.getPassword().getBytes()));

        sysUserMapper.save(sysUser);
    }

    @Override
    public void updateSysUser(SysUser sysUser) {
        sysUserMapper.update(sysUser);
    }

    @Override
    public void deleteById(Long userId) {
        sysUserMapper.deleteById(userId);
    }

    @Override
    public void assignRoleAuth(AssignRoleDto assignRoleDto) {
        // 删除之前所有的授权关系
        sysRoleUserMapper.deleteByUserId(assignRoleDto.getUserId());

        // 重新分配新的角色数据
        List<Long> roleIdList = assignRoleDto.getRoleIdList();
        roleIdList.forEach(roleId -> sysRoleUserMapper.assignRoleAuth(assignRoleDto.getUserId(), roleId));
    }

    /**
     * 校验图片验证码
     *
     * @param loginDto 登录DTO
     */
    private void checkValidateCode(LoginDto loginDto) {
        // 1、获取输入的验证码和存储到redis的key名称
        String captcha = loginDto.getCaptcha();
        String key = loginDto.getCodeKey();

        //2、根据获取redis里面的key，查询redis里面存储的验证码 @see ValidateCodeServiceImpl.generateValidateCode
        String redisCode = redisTemplate.opsForValue().get("user:validate" + key);

        // 3、比较输入的验证码和redis存储的验证码是否一致
        if (StrUtil.isEmpty(redisCode) || !StrUtil.equalsIgnoreCase(redisCode, captcha)) {
            // 4、不一致则提示用户校验失败
            throw new SpzxCustomException(ResultCodeEnum.VALIDATECODE_ERROR);
        }

        //5、如果一致，删除redis里面验证码
        redisTemplate.delete("user:validate" + key);
    }
}
